/*
	SnowstormMain.cpp

	Contains main initialisation and control functions for the Snowstorm engine

	Author: Aidan Goddard 25/7/14
*/

#include"Snowstorm.h"
#include<Window.h>
#pragma comment(lib, "libwindow64.lib")

///////////////////////////////////////////////
// functions from other systems
extern void InitInput(Snowstorm::Window *wnd);
extern void InputUpdate();
extern void GetXInputStates();

///////////////////////////////////////////////
// data
static volatile uint32_t message_handler_ready_signal = 0;
static HANDLE message_handler_thread;
static HANDLE sync_mutex;
static Snowstorm::Window *wnd = NULL;
ENGINE_VARS *ev = NULL;
static double windows_timer_frequecy;
static double seconds_per_frame;

///////////////////////////////////////////////
// window focus losing handler
static void FocusLost(Snowstorm::Window *wnd, WPARAM wparam, LPARAM lparam)
{
	wnd->Minimize();
	return;
}

///////////////////////////////////////////////
// WinAPI message handler thread functions
DWORD WINAPI MessageHandlerMain(LPVOID lpparam)
{
	// create the engine window
	if(Snowstorm::Window::Initialise(Snowstorm::w_mode_t::MODE_SINGLE) != INIT_SUCCESS)
		return ENGINE_ERROR_FAIL;
	Snowstorm::WindowAttributes wa;
	wa.catch_input = true;
	wa.height = v(window_height);
	wa.width = v(window_width);
	wa.title = v(window_title);
	wa.parent_hwnd = NULL;
	if(v(screen_type) == SCREEN_TYPE::EXCLUSIVE_FULLSCREEN)
		wa.window_type = Snowstorm::TYPE_NO_BOARDER;
	else if(v(screen_type) == SCREEN_TYPE::FULLSCREEN)
		wa.window_type = Snowstorm::TYPE_FULLSCREEN;
	else
		wa.window_type = Snowstorm::TYPE_POPUP;
	wa.x = 768;
	wa.y = 128;

	wnd = Snowstorm::Window::Create(&wa);
	if(!wnd)
		return ENGINE_ERROR_FAIL;

	// show the window
	wnd->Show();
	
	// setup input grabbing controls
	InitInput(wnd);

	// wait on signal to start
	message_handler_ready_signal = 1;
	WaitForSingleObject(sync_mutex, INFINITE);
	ReleaseMutex(sync_mutex);

	// loop on message handler
	while(Snowstorm::Window::MessageLoop())
	{
		// check the engine state
		if(!v(engine_running))
			break;

		// update from XInput
		GetXInputStates();

	}

	return 0;
}

///////////////////////////////////////////////
// init

ENGINE_VARS *EngineInitialise()
{
	// create the engine vars stucture
	ev = new ENGINE_VARS;
	if(!ev)
		return NULL;

	// initialise window controls
	ev->window_width = 1024;
	ev->window_height = 768;
	ev->screen_type = SCREEN_TYPE::WINDOWED;
	ev->minimize_on_lost_focus = false;
	ev->lock_cursor_to_window = false;
	ev->hide_cursor = false;
	ev->window_title = L"Snowstorm Engine v0.1a";

	// initialise frame controls
	ev->target_FPS = 60;
	ev->current_FPS = 60;
	ev->vsync = false;
	ev->frame_time = 0.167f;
	ev->frame_idle_time = 0.0f;

	// gloabl controls
	ev->engine_running = false;

	return ev;
}

///////////////////////////////////////////////
// start engine

SNOWSTORMDLL_API uint32_t EngineStart(ENGINE_VARS *_ev)
{
	// save the engine vars pointer
	ev = _ev;

	// get the windows peformance counter frequency
	LARGE_INTEGER t;
	QueryPerformanceFrequency(&t);
	windows_timer_frequecy = (double)t.QuadPart;

	// setup seconds per frame counter
	seconds_per_frame = 1.0 / ((double)v(target_FPS));

	// create thread syncronisation object and own it
	sync_mutex = CreateMutex(NULL, TRUE, NULL);

	// setup thread to handle the window messages
	message_handler_thread = CreateThread(NULL, 0, MessageHandlerMain, 0, 0, NULL);
	if(!message_handler_thread)
		return ENGINE_ERROR_FAIL;

	// set engine state to running
	v(engine_running) = true;

	// wait for all worker threads to signal ready
	while(!message_handler_ready_signal){}

	// send start signals to threads (release synchronisation object)
	ReleaseMutex(sync_mutex);

	// finished
	return ENGINE_ERROR_SUCCESS;
}

///////////////////////////////////////////////
// stop engine

SNOWSTORMDLL_API uint32_t EngineStop()
{
	// set the engine state
	v(engine_running) = false;

	return 1;
}

///////////////////////////////////////////////
// perform frame function

SNOWSTORMDLL_API uint32_t EngineFrame()
{
	// check engine state
	if(!v(engine_running))
	{
		// engine is no longer running	
		return 0;
	}

	// start frame timer
	LARGE_INTEGER t;
	QueryPerformanceCounter(&t);
	double start_time = (double)t.QuadPart;
	double target_time = start_time + (windows_timer_frequecy * seconds_per_frame);

	// handle input
	InputUpdate();

	
	// finished stuff
	// do timing things
	QueryPerformanceCounter(&t);
	v(frame_idle_time) = (target_time - t.QuadPart) / windows_timer_frequecy;

	if(v(vsync))
	{
		// wait on DirectX vsync
	}
	else if(t.QuadPart < target_time)
	{
		// wait on timer
		while(1)
		{
			QueryPerformanceCounter(&t);
			if(target_time <= t.QuadPart)
				break;
		}
	}

	// get the frame time in seconds
	QueryPerformanceCounter(&t);
	v(frame_time) = (t.QuadPart - start_time) / windows_timer_frequecy;

	// get the FPS
	static float FPS_counter = 0; 
	static float time_counter = 0;
	if(time_counter > 1)
	{
		v(current_FPS) = FPS_counter;
		FPS_counter = 0;
		time_counter = 0;
	}
	else
	{
		time_counter += v(frame_time);
		FPS_counter++;
	}

	return 1;
}

